package cn.sh.ideal.yedis.core;

import cn.sh.ideal.yedis.builder.BaseBuilder;
import cn.sh.ideal.yedis.util.Assert;
import cn.sh.ideal.yedis.util.RedisLock;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCluster;
import redis.clients.util.Pool;

/**
 * Helper class that simplifies Redis data access code.
 * @author WYY
 *
 * @param <V> the Redis value type against which the template works
 */
@SuppressWarnings({"unchecked","rawtypes"})
public class RedisTemplate<V> {
	private RedisMode redisModel = RedisMode.STANDALONE;
	private BaseBuilder builder;

	private Pool<Jedis> jedisPool;

	private JedisCluster jedisCluster;

	private RedisOperation<V> operation;

	public enum RedisMode{
		STANDALONE,SENTINEL,CLUSTER
	}
	
	public RedisLock getRedisLock(String key) {
		return new RedisLock(key,operation);
	}
	
	public <T> T execute(RedisCallback<Jedis,T> callback) {
		Assert.notNull(callback, "Callback object must not be null");
		Jedis jedis = null;
		try {
			jedis = jedisPool.getResource();
			T result = callback.doInRedis(jedis);
			return result;
		}finally {
			if(null != jedis) {
				jedis.close();
			}
		}
	}

	public <T> T executeCluster(RedisCallback<JedisCluster,T> callback) {
		Assert.notNull(callback, "Callback object must not be null");
		T result = callback.doInRedis(jedisCluster);
		return result;
	}

	/**
	 * 订阅
	 * @param subcriber
	 * @param channel
	 */
	public void subcribe(Subcriber<V> subcriber,final String channel) {
		operation.subcribe(subcriber, channel, jedisPool);
	}
	
	/**
	 * 取消订阅
	 * @param subcriber
	 * @param channel
	 */
	public void unsubcribe(Subcriber<V> subcriber) {
		operation.unsubcribe(subcriber);
	}

	public RedisOperation<V> getOperation() {
		return operation;
	}


	public RedisMode getRedisModel() {
		return redisModel;
	}


	public RedisTemplate<V> setRedisModel(RedisMode redisModel) {
		this.redisModel = redisModel;
		return this;
	}

	public RedisTemplate<V> setBuilder(BaseBuilder builder) {
		this.builder = builder;
		return this;
	}
	
	public RedisTemplate<V> init() {
		Object object = builder.init();

		if(RedisMode.CLUSTER.equals(redisModel)) {
			jedisCluster  = (JedisCluster)object;
			operation = new ClusterOperation<>(this);
		}else {
			jedisPool = (Pool<Jedis>)object;
			operation = new JedisOperation<>(this);
		}
		
		return this;
	}
	
	public Jedis getJedis() {
		return jedisPool.getResource();
	}

}
